import { execSync as exec } from "child_process";
import fs, { mkdirSync, rmdirSync } from "fs";
import path from "path";
import posConfig from "./pos.config.json";
import poaConfig from "./poa.config.json";

export class AWSUploadSingle {
    private config: typeof posConfig | typeof poaConfig;

    constructor(consensus: "poa" | "pos", private dataPath = __dirname, private password: string, private secretId: string) {
        if (consensus === "poa") {
            this.config = poaConfig;
        } else if (consensus === "pos") {
            this.config = posConfig;
        } else {
            throw Error("Consensus not supported");
        }
    }

    async exportValidatorKeys(nodeName: string) {
        const tmpFolder = path.join(this.dataPath, `${nodeName}-tmp`);
        mkdirSync(tmpFolder);
        await exec(
            `docker run --rm -v ${path.join(this.dataPath, nodeName)}:/root/.exrpd ${this.config.dockerImage} sh -c 'echo ${
                this.password
            } | exrpd keys export ${this.config.node.key_name}' > ${path.join(tmpFolder, `${nodeName}.key`)}`,
        );
        await exec(
            `cp ${path.join(this.dataPath, `/${nodeName}/config/priv_validator_key.json`)} ${path.join(
                tmpFolder,
                `priv_${nodeName}.json`,
            )}`,
        );
        await exec(
            `cp ${path.join(this.dataPath, `/${nodeName}/config/node_key.json`)} ${path.join(tmpFolder, `node_key_${nodeName}.json`)}`,
        );
        await exec(`cd ${tmpFolder} && tar -czf ../${nodeName}.tar.gz *`);
        rmdirSync(tmpFolder, { recursive: true });
    }

    async addAwsSecret(key: string, value: string) {
        const currentRawSecrets = exec(
            `aws secretsmanager get-secret-value --secret-id=${this.secretId} --query SecretString --output=json`,
        );
        const currentSecrets = JSON.parse(JSON.parse(currentRawSecrets.toString()));
        currentSecrets[key] = value;
        exec(
            `aws secretsmanager put-secret-value --secret-id ${this.secretId} --secret-string ${JSON.stringify(
                JSON.stringify(currentSecrets),
            )}`,
        );
    }

    async uploadNodeFiles(nodeName: string) {
        const nodeFilesTar = path.join(this.dataPath, nodeName + ".tar.gz");
        const filesB64 = fs.readFileSync(nodeFilesTar).toString("base64");
        await this.addAwsSecret(nodeName, filesB64);
        exec(`rm ${nodeFilesTar}`);
    }

    async uploadGenesis() {
        const genesisPath = path.join(this.dataPath, "genesis.json");
        const genesis = JSON.stringify(JSON.parse(fs.readFileSync(genesisPath).toString()));
        await this.addAwsSecret("genesis", genesis);
    }

    async uploadNodes() {
        const nodesPath = path.join(this.dataPath, "nodes.json");
        const nodes = JSON.stringify(JSON.parse(fs.readFileSync(nodesPath).toString()));
        await this.addAwsSecret("nodes", nodes);
    }

    async run() {
        const nodes = JSON.parse(fs.readFileSync(path.join(this.dataPath, "nodes.json")).toString());
        for (const node of nodes) {
            await this.exportValidatorKeys(node.name);
            await this.uploadNodeFiles(node.name);
        }
        await this.uploadGenesis();
        await this.uploadNodes();
    }
}

export class AWSUpload {
    private config: typeof posConfig | typeof poaConfig;

    constructor(consensus: "poa" | "pos", private dataPath = __dirname, private password: string, private secretIds: string[]) {
        if (consensus === "poa") {
            this.config = poaConfig;
        } else if (consensus === "pos") {
            this.config = posConfig;
        } else {
            throw Error("Consensus not supported");
        }
    }

    async exportValidatorKeys(nodeName: string) {
        const nodeFolder = path.join(this.dataPath, `${nodeName}`);
        await exec(`cd ${nodeFolder} && tar -czf ../${nodeName}.tar.gz *`);
    }

    async addAwsSecret(secretId: string, key: string, value: string) {
        const currentRawSecrets = exec(`aws secretsmanager get-secret-value --secret-id=${secretId} --query SecretString --output=json`);
        const currentSecrets = JSON.parse(JSON.parse(currentRawSecrets.toString()));
        currentSecrets[key] = value;
        exec(
            `aws secretsmanager put-secret-value --secret-id ${secretId} --secret-string ${JSON.stringify(JSON.stringify(currentSecrets))}`,
        );
    }

    async uploadNodeFiles(secretId: string, nodeName: string) {
        const nodeFilesTar = path.join(this.dataPath, nodeName + ".tar.gz");
        const filesB64 = fs.readFileSync(nodeFilesTar).toString("base64");
        await this.addAwsSecret(secretId, nodeName, filesB64);
        exec(`rm ${nodeFilesTar}`);
    }

    async run() {
        console.log("7 - Uploading node files to AWS Secret Manager");
        const nodes = JSON.parse(fs.readFileSync(path.join(this.dataPath, "nodes.json")).toString());
        if (nodes.length !== this.secretIds.length) throw new Error("SecretIds length is not the same than nodes length");
        for (let i = 0; i < nodes.length; i++) {
            await this.exportValidatorKeys(nodes[i].name);
            await this.uploadNodeFiles(this.secretIds[i], nodes[i].name);
        }
    }
}
